<?php

namespace App\Models;

use App\Enums\AccountAction;
use App\Util;
use Carbon\Carbon;
use Dcat\Admin\Traits\HasDateTimeFormatter;

use Exception;
use Illuminate\Database\Eloquent\Model;

/**
 * App\Models\Account
 *
 * @property int $id
 * @property int $user_id
 * @property string $coin
 * @property string|null $free
 * @property string|null $lock
 * @property string|null $address
 * @property int|null $version
 * @property string|null $created_at
 * @property string|null $updated_at
 * @property-read \App\Models\User|null $user
 * @method static \Illuminate\Database\Eloquent\Builder|Account newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|Account newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|Account query()
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereAddress($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereCoin($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereFree($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereLock($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereUpdatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereUserId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Account whereVersion($value)
 * @mixin \Eloquent
 */
class Account extends Model
{
	use HasDateTimeFormatter;
    public $timestamps = false;

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }

    /**
     * @param mixed $amount
     * @throws Exception
     */
    public function freeIncr($amount, AccountAction $type, Model $action)
    {
        Util::mustInTransaction();

        $free = $this->free;
        $this->free = bcadd($free, $amount, 8);
        ++$this->version;
        $this->save();

        $log = new AccountLog();
        $log->account_id = $this->id;
        $log->version = $this->version;
        $log->type = $type->name;
        $log->action_type = $action::class;
        $log->action_id = $action->{$action->getKeyName()};
        $log->free_before = $free;
        $log->free_change = $amount;
        $log->free_after = $this->free;
        $log->lock_before = $this->getOriginal('lock');
        $log->lock_change = 0;
        $log->lock_after = $this->lock;
        $log->created_at = Carbon::now();
        $log->save();
    }

    /**
     * @param mixed $amount
     * @throws Exception
     */
    public function freeDecr($amount, AccountAction $type, Model $action)
    {
        Util::mustInTransaction();

        $free = $this->free;
        $this->free = bcsub($free, $amount, 8);
        ++$this->version;
        $this->save();

        $log = new AccountLog();
        $log->account_id = $this->id;
        $log->version = $this->version;
        $log->type = $type->name;
        $log->action_type = $action::class;
        $log->action_id = $action->{$action->getKeyName()};
        $log->free_before = $free;
        $log->free_change = bcmul('-1', $amount, 8);
        $log->free_after = $this->free;
        $log->lock_before = $this->getOriginal('lock');
        $log->lock_change = 0;
        $log->lock_after = $this->lock;
        $log->created_at = Carbon::now();
        $log->save();
    }

    /**
     * @param mixed $amount
     * @throws Exception
     */
    public function lockIncr($amount, AccountAction $type, Model $action)
    {
        Util::mustInTransaction();

        $lock = $this->lock;
        $this->lock = bcadd($lock, $amount, 8);
        ++$this->version;
        $this->save();

        $log = new AccountLog();
        $log->account_id = $this->id;
        $log->version = $this->version;
        $log->type = $type->name;
        $log->action_type = $action::class;
        $log->action_id = $action->{$action->getKeyName()};
        $log->free_before = $this->getOriginal('free');
        $log->free_change = 0;
        $log->free_after = $this->free;
        $log->lock_before = $lock;
        $log->lock_change = $amount;
        $log->lock_after = $this->lock;
        $log->created_at = Carbon::now();
        $log->save();
    }

    /**
     * @param mixed $amount
     * @throws Exception
     */
    public function lockDecr($amount, AccountAction $type, Model $action)
    {
        Util::mustInTransaction();

        $lock = $this->lock;
        $this->lock = bcsub($lock, $amount, 8);
        ++$this->version;
        $this->save();

        $log = new AccountLog();
        $log->account_id = $this->id;
        $log->version = $this->version;
        $log->type = $type->name;
        $log->action_type = $action::class;
        $log->action_id = $action->{$action->getKeyName()};
        $log->free_before = $this->getOriginal('free');
        $log->free_change = 0;
        $log->free_after = $this->free;
        $log->lock_before = $lock;
        $log->lock_change = bcmul('-1', $amount, 8);
        $log->lock_after = $this->lock;
        $log->created_at = Carbon::now();
        $log->save();
    }

    /**
     * @param mixed $amount
     * @throws Exception
     */
    public function free2lock($amount, AccountAction $type, Model $action)
    {
        Util::mustInTransaction();
        $lock = $this->lock;
        $free = $this->free;
        $this->free = bcsub($free, $amount, 8);
        $this->lock = bcadd($lock, $amount, 8);
        ++$this->version;
        $this->save();

        $log = new AccountLog();
        $log->account_id = $this->id;
        $log->version = $this->version;
        $log->type = $type->name;
        $log->action_type = $action::class;
        $log->action_id = $action->{$action->getKeyName()};
        $log->free_before = $free;
        $log->free_change = bcmul('-1', $amount, 8);
        $log->free_after = $this->free;
        $log->lock_before = $lock;
        $log->lock_change = $amount;
        $log->lock_after = $this->lock;
        $log->created_at = Carbon::now();
        $log->save();
    }

    /**
     * @param mixed $amount
     * @throws Exception
     */
    public function lock2free($amount, AccountAction $type, Model $action)
    {
        Util::mustInTransaction();

        $lock = $this->lock;
        $free = $this->free;
        $this->lock = bcsub($lock, $amount, 8);
        $this->free = bcadd($free, $amount, 8);
        ++$this->version;
        $this->save();

        $log = new AccountLog();
        $log->account_id = $this->id;
        $log->version = $this->version;
        $log->type = $type->name;
        $log->action_type = $action::class;
        $log->action_id = $action->{$action->getKeyName()};
        $log->free_before = $free;
        $log->free_change = $amount;
        $log->free_after = $this->free;
        $log->lock_before = $lock;
        $log->lock_change = bcmul('-1', $amount, 8);
        $log->lock_after = $this->lock;
        $log->created_at = Carbon::now();
        $log->save();
    }
}
